﻿using System;
using System.Collections.Generic;

using Ewk.Math.ComputationTypes.Factories;
using Ewk.Math.Numerics;

namespace Ewk.Math.ComputationTypes
{
    /// <summary>
    /// Holds and retrieves instances of <see cref="IComputableFactory{T}"/>.
    /// </summary>
    public static class ComputableFactoryProvider
    {
        static readonly object _thisLock = new object();
        static readonly Dictionary<Type, object> _factories = new Dictionary<Type, object>();

        static ComputableFactoryProvider()
        {
            _factories.Add(typeof(double), new ComputableDoubleFactory());
            _factories.Add(typeof(int), new ComputableInt32Factory());
            _factories.Add(typeof(Complex), new ComputableComplexFactory());

            InitializeComputableFunctionFactory<double>();
            InitializeComputableFunctionFactory<short>();
            InitializeComputableFunctionFactory<int>();
            InitializeComputableFunctionFactory<Complex>();
        }

        static void InitializeComputableFunctionFactory<T>()
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            _factories.Add(typeof(Function<double, T>), new ComputableFunctionFactory<double, T>());
            _factories.Add(typeof(Function<int, T>), new ComputableFunctionFactory<int, T>());
            _factories.Add(typeof(Function<Complex, T>), new ComputableFunctionFactory<Complex, T>());
        }

        internal static IComputableFactory<T> ResolveFactory<T>()
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            if (!_factories.ContainsKey(typeof(T)))
            {
                throw new InvalidOperationException("The requested factory is not supported.");
            }

            return _factories[typeof(T)] as IComputableFactory<T>;
        }

        /// <summary>
        /// Registers a <see cref="IComputableFactory{T}"/> to the <see cref="ComputableFactoryProvider"/>.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the internal value of the <see cref="Computable{T}"/>.</typeparam>
        /// <param name="factory">The factory to be registered.</param>
        /// <returns>A value that indicates whether the registry succeeded or not.</returns>
        public static bool RegisterFactory<T>(IComputableFactory<T> factory)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            lock (_thisLock)
            {
                if (_factories.ContainsKey(typeof (T)))
                {
                    return false;
                }

                _factories.Add(typeof (T), factory);
            }

            return true;
        }
    }
}